// src/gateways/chat.gateway.ts
import {
  WebSocketGateway,
  WebSocketServer,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
  SubscribeMessage,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
  namespace: 'chat',
  cors: {
    origin: '*',
    methods: ['GET', 'POST'],
    credentials: true,
  },
  path: '/socket.io',
  options: {
    pingTimeout: 60000,
  },
})
export class ChatGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  // 存储连接的用户信息
  private connectedClients = new Map<string, { 
    userId: string; 
    username: string; 
    connectedAt: Date;
    isIdentified: boolean; // 标记是否已完成身份识别
  }>();

  // 用户ID到客户端ID的映射
  private userIdToClientId = new Map<string, string>();

  // 房间用户管理
  private roomUsers = new Map<string, Set<string>>();
  
  // 消息频率限制
  private messageTimestamps = new Map<string, number[]>();

  afterInit(server: Server) {
    console.log('Socket.IO 网关初始化完成');
  }

  handleConnection(client: Socket) {
    console.log('客户端连接:', client.id);
    
    // 初始化为匿名用户，标记为未识别
    this.connectedClients.set(client.id, {
      userId: `anonymous_${client.id}`,
      username: `匿名用户_${client.id.substring(0, 8)}`,
      connectedAt: new Date(),
      isIdentified: false
    });

    this.userIdToClientId.set(`anonymous_${client.id}`, client.id);

    // 只发送当前在线用户列表给新连接的客户端，不广播上线通知
    client.emit('onlineUsers', {
      count: this.getIdentifiedUsersCount(),
      users: this.getOnlineUsers()
    });

    client.emit('connection', { 
      message: '连接成功', 
      clientId: client.id,
      isAnonymous: true,
      requireIdentify: true
    });
  }

  handleDisconnect(client: Socket) {
    console.log('客户端断开:', client.id);
    
    const disconnectedUser = this.connectedClients.get(client.id);
    
    // 清理数据
    if (disconnectedUser) {
      this.userIdToClientId.delete(disconnectedUser.userId);
      
      // 只有已识别的用户才广播离线通知
      if (disconnectedUser.isIdentified && disconnectedUser.userId !== `anonymous_${client.id}`) {
        this.server.emit('userOffline', {
          userId: disconnectedUser.userId,
          username: disconnectedUser.username,
          clientId: client.id
        });
      }
    }
    
    this.connectedClients.delete(client.id);
    this.messageTimestamps.delete(client.id);

    // 清理房间中的用户
    this.roomUsers.forEach((users, room) => {
      if (users.has(client.id)) {
        users.delete(client.id);
        const userInfo = this.connectedClients.get(client.id);
        if (userInfo?.isIdentified) {
          this.server.to(room).emit('userLeft', { 
            clientId: client.id,
            userId: userInfo.userId,
            username: userInfo.username
          });
        }
      }
    });

    // 更新在线用户列表（只包含已识别用户）
    this.broadcastOnlineUsers();
  }

  @SubscribeMessage('identify')
  handleIdentify(
    client: Socket,
    payload: { userId: string; username: string }
  ) {
    try {
      if (!payload.userId || !payload.username) {
        client.emit('error', { message: '用户ID和用户名不能为空' });
        return;
      }

      const oldUserInfo = this.connectedClients.get(client.id);
      
      // 清理旧的用户ID映射
      if (oldUserInfo) {
        this.userIdToClientId.delete(oldUserInfo.userId);
      }
      
      // 更新用户信息，标记为已识别
      this.connectedClients.set(client.id, {
        userId: payload.userId,
        username: payload.username,
        connectedAt: oldUserInfo?.connectedAt || new Date(),
        isIdentified: true
      });

      // 更新用户ID映射
      this.userIdToClientId.set(payload.userId, client.id);

      // 通知用户识别成功
      client.emit('identified', { 
        success: true, 
        message: '身份识别成功',
        userInfo: {
          userId: payload.userId,
          username: payload.username
        }
      });

      // 广播用户上线通知（只有完成身份识别后才广播）
      this.server.emit('userOnline', {
        userId: payload.userId,
        username: payload.username,
        clientId: client.id,
        connectedAt: new Date().toISOString()
      });

      // 广播在线用户列表更新
      this.broadcastOnlineUsers();

      console.log(`用户 ${payload.username} (${payload.userId}) 上线`);

    } catch (error) {
      console.error('身份识别错误:', error);
      client.emit('error', { message: '身份识别失败' });
    }
  }

  @SubscribeMessage('updateProfile')
  handleUpdateProfile(
    client: Socket,
    payload: { username: string; avatar?: string; status?: string }
  ) {
    try {
      if (!payload.username) {
        client.emit('error', { message: '用户名不能为空' });
        return;
      }

      const oldUserInfo = this.connectedClients.get(client.id);
      if (!oldUserInfo || !oldUserInfo.isIdentified) {
        client.emit('error', { message: '请先进行身份识别' });
        return;
      }

      // 更新用户信息
      const newUserInfo = {
        ...oldUserInfo,
        username: payload.username
      };
      this.connectedClients.set(client.id, newUserInfo);

      // 广播用户信息更新
      this.server.emit('userProfileUpdated', {
        userId: oldUserInfo.userId,
        oldUsername: oldUserInfo.username,
        newUsername: payload.username,
        clientId: client.id,
        avatar: payload.avatar,
        status: payload.status,
        updatedAt: new Date().toISOString()
      });

      // 更新在线用户列表
      this.broadcastOnlineUsers();

      client.emit('profileUpdated', {
        success: true,
        message: '个人信息更新成功',
        userInfo: newUserInfo
      });

    } catch (error) {
      console.error('更新个人信息错误:', error);
      client.emit('error', { message: '更新个人信息失败' });
    }
  }

  @SubscribeMessage('getOnlineUsers')
  handleGetOnlineUsers(client: Socket) {
    try {
      const onlineUsers = this.getOnlineUsers();
      client.emit('onlineUsers', {
        count: onlineUsers.length,
        users: onlineUsers,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      console.error('获取在线用户错误:', error);
      client.emit('error', { message: '获取在线用户失败' });
    }
  }

  @SubscribeMessage('privateMessage')
  handlePrivateMessage(
    client: Socket,
    payload: {
      toUserId: string;
      toUsername?: string;
      message: string;
    }
  ) {
    try {
      // 验证消息
      if (!payload.toUserId) {
        client.emit('error', { message: '接收者用户ID不能为空' });
        return;
      }

      if (!payload.message?.trim()) {
        client.emit('error', { message: '消息内容不能为空' });
        return;
      }

      // 获取发送者信息
      const senderInfo = this.connectedClients.get(client.id);
      if (!senderInfo || !senderInfo.isIdentified) {
        client.emit('error', { message: '请先进行身份识别' });
        return;
      }

      // 查找接收者的客户端ID
      const receiverClientId = this.userIdToClientId.get(payload.toUserId);
      if (!receiverClientId) {
        client.emit('error', { message: '用户不在线或不存在' });
        return;
      }

      // 检查接收者是否已识别身份
      const receiverInfo = this.connectedClients.get(receiverClientId);
      if (!receiverInfo || !receiverInfo.isIdentified) {
        client.emit('error', { message: '用户不在线或未完成身份识别' });
        return;
      }

      // 检查是否给自己发消息
      if (payload.toUserId === senderInfo.userId) {
        client.emit('error', { message: '不能给自己发送私信' });
        return;
      }

      // 创建消息对象
      const messageData = {
        fromUserId: senderInfo.userId,
        fromUsername: senderInfo.username,
        toUserId: payload.toUserId,
        toUsername: receiverInfo.username,
        message: payload.message,
        timestamp: new Date().toISOString(),
        isPrivate: true
      };

      // 发送给接收者
      this.server.to(receiverClientId).emit('privateMessage', messageData);

      // 同时发送给发送者（作为发送确认）
      client.emit('privateMessageSent', {
        ...messageData,
        status: 'delivered',
        deliveredAt: new Date().toISOString()
      });

      console.log('私信发送:', {
        from: senderInfo.username,
        to: receiverInfo.username,
        message: payload.message
      });

    } catch (error) {
      console.error('发送私信错误:', error);
      client.emit('error', { message: '发送私信失败' });
    }
  }

  @SubscribeMessage('chatMessage')
  handleChatMessage(
    client: Socket,
    payload: { message: string; username: string; timestamp: string },
  ) {
    try {
      // 速率限制
      const now = Date.now();
      const timestamps = this.messageTimestamps.get(client.id) || [];
      const recentMessages = timestamps.filter(time => now - time < 1000);
      
      if (recentMessages.length >= 5) {
        client.emit('error', { message: '消息发送过于频繁' });
        return;
      }
      
      timestamps.push(now);
      this.messageTimestamps.set(client.id, timestamps.slice(-10));
      
      // 验证消息
      if (!payload.message?.trim()) {
        client.emit('error', { message: '消息内容不能为空' });
        return;
      }

      const userInfo = this.connectedClients.get(client.id);
      const username = userInfo?.username || payload.username || '匿名用户';
      
      console.log('收到消息：', { message: payload.message, username });
      
      // 广播给所有客户端（除发送者外）
      client.broadcast.emit('newMessage', {
        message: payload.message,
        username: username,
        timestamp: new Date().toISOString(),
        sender: client.id,
        userId: userInfo?.userId,
        isPrivate: false
      });

    } catch (error) {
      console.error('处理消息错误:', error);
      client.emit('error', { message: '服务器处理消息时出错' });
    }
  }

  @SubscribeMessage('joinRoom')
  handleJoinRoom(client: Socket, room: string) {
    try {
      if (!room.trim()) {
        client.emit('error', { message: '房间名不能为空' });
        return;
      }

      if (!this.roomUsers.has(room)) {
        this.roomUsers.set(room, new Set());
      }
      
      this.roomUsers?.get(room)?.add(client.id);
      void client.join(room);
      
      const userInfo = this.connectedClients.get(client.id);

      client.emit('roomJoined', { 
        room: room,
        message: `已加入房间：${room}`
      });

      // 通知房间内其他用户
      client.to(room).emit('userJoined', {
        clientId: client.id,
        userId: userInfo?.userId,
        username: userInfo?.username,
        room: room
      });

    } catch (error) {
      console.error('加入房间错误:', error);
      client.emit('error', { message: '加入房间失败' });
    }
  }

  @SubscribeMessage('roomMessage')
  handleRoomMessage(client: Socket, data: { room: string; message: string }) {
    try {
      if (!data.message?.trim()) {
        client.emit('error', { message: '消息内容不能为空' });
        return;
      }

      if (!data.room?.trim()) {
        client.emit('error', { message: '房间名不能为空' });
        return;
      }

      const userInfo = this.connectedClients.get(client.id);
      
      // 仅向指定房间内的客户端发送消息
      this.server.to(data.room).emit('roomNewMessage', {
        message: data.message,
        room: data.room,
        sender: client.id,
        userId: userInfo?.userId,
        username: userInfo?.username,
        timestamp: new Date().toISOString(),
        isPrivate: false
      });
      
    } catch (error) {
      console.error('发送房间消息错误:', error);
      client.emit('error', { message: '发送消息失败' });
    }
  }

  @SubscribeMessage('leaveRoom')
  handleLeaveRoom(client: Socket, room: string) {
    try {
      if (this.roomUsers.has(room)) {
        this.roomUsers?.get(room)?.delete(client.id);
      }
      
      void client.leave(room);
      
      const userInfo = this.connectedClients.get(client.id);
      
      client.emit('roomLeft', { room: room });
      
      // 通知房间内其他用户
      client.to(room).emit('userLeft', {
        clientId: client.id,
        userId: userInfo?.userId,
        username: userInfo?.username,
        room: room
      });

    } catch (error) {
      console.error('离开房间错误:', error);
      client.emit('error', { message: '离开房间失败' });
    }
  }

  @SubscribeMessage('getUserSocketInfo')
  handleGetUserSocketInfo(client: Socket, userId: string) {
    try {
      const clientId = this.userIdToClientId.get(userId);
      if (!clientId) {
        client.emit('userSocketInfo', { 
          userId, 
          isOnline: false,
          message: '用户不在线'
        });
        return;
      }

      const userInfo = this.connectedClients.get(clientId);
      // 只返回已识别的用户信息
      if (!userInfo || !userInfo.isIdentified) {
        client.emit('userSocketInfo', { 
          userId, 
          isOnline: false,
          message: '用户未完成身份识别'
        });
        return;
      }

      client.emit('userSocketInfo', {
        userId,
        clientId,
        username: userInfo.username,
        isOnline: true,
        connectedAt: userInfo.connectedAt
      });

    } catch (error) {
      console.error('获取用户Socket信息错误:', error);
      client.emit('error', { message: '获取用户信息失败' });
    }
  }

  // 广播在线用户列表给所有客户端（只包含已识别用户）
  private broadcastOnlineUsers() {
    const onlineUsers = this.getOnlineUsers();
    this.server.emit('onlineUsersUpdate', {
      count: onlineUsers.length,
      users: onlineUsers,
      timestamp: new Date().toISOString()
    });
  }

  // 获取在线用户列表（只返回已识别的用户）
  private getOnlineUsers() {
    return Array.from(this.connectedClients.entries())
      .filter(([_, user]) => user.isIdentified && user.userId !== `anonymous_${_[0]}`)
      .map(([clientId, user]) => ({
        clientId,
        userId: user.userId,
        username: user.username,
        connectedAt: user.connectedAt,
        isOnline: true
      }));
  }

  // 获取已识别用户数量
  private getIdentifiedUsersCount(): number {
    return Array.from(this.connectedClients.values())
      .filter(user => user.isIdentified && user.userId !== `anonymous_${user.userId}`)
      .length;
  }
}